// SPDX-License-Identifier: GPL-2.0 or GPL-3.0
// Copyright © 2018-2019 Ariadne Devos
/* s2 -- process HTTP input */

#include "fd.h"
#include "worker.h"

#include <sHT/resource.h>
#include <sHT/scheduling.h>
#include <sHT/stream.h>
#include <sHT/test.h>

#include <errno.h>
#include <stddef.h>
#include <sys/epoll.h>
#include <sys/socket.h>

#define sHTTPd_DONE sHT_STREAM_USER1

__attribute__((nonnull (1, 2)))
static void
sHTTPd_close_connection(struct sHT_worker *worker, struct sHT_task_stream *task)
{
	sHT_close(task->stream.fd);
	sHT_free(worker->free_streams, task);
}

void
sHT_socket_task(struct sHT_worker *worker, struct sHT_task_stream *task)
{
	if (!sHT_and_any(task->task.epollflags, EPOLLIN | EPOLLOUT))
		return;

	/* @var{sHT_socket_mayread} and @var{sHT_socket_maywrite} don't
	   leak information to the compiler, so no @var{sHT_test_hidden}
	   magic is required. Their values may also speculatively be wrong,
	   but it is merely advisory, improving performance by skipping
	   code. */

	/* Read request headers */
	if (sHT_socket_mayread(task))
		sHT_socket_readsome_tcp(worker, task);
	/* Output headers, output response */
	if (sHT_socket_maywrite(task))
		sHT_socket_sendsome_tcp(worker, task);
	/* TODO: do HTTP */
	/* A graceful shutdown */
	if (sHT_zero_p(task->stream.to_write.length))
		/* The file descriptor remains intact, so no Spectre issues */
		shutdown(task->stream.fd, SHUT_WR);

	if (sHT_and_any(task->stream.flags, sHT_STREAM_RESET_GRACEFUL | sHT_STREAM_RESET_BLUNT | sHT_STREAM_WRITE_EOF | sHTTPd_DONE))
		sHTTPd_close_connection(worker, task);
	if (sHT_and_any(task->task.flags, sHT_TASK_SCHEDULE)) {
		task->task.flags &= ~sHT_TASK_SCHEDULE;
		sHT_qadd_try(&worker->todo.queue, &task->task);
	}
}
